﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;

namespace CommonUtil
{
    #region 属性
    /// <summary>
    /// 通讯类型
    /// </summary>
    public enum TypeSocket
    {
        /// <summary>
        /// 发送的类型为消息
        /// </summary>
        MESSAGES,
        /// <summary>
        /// 发送的类型为FileInfo
        /// </summary>
        FILE
    }

    /// <summary>
    /// 请求命令字
    /// </summary>
    public enum MesCmd
    {
        /// <summary>
        /// 请求数据库连接串 帐套名~是否默认帐套~数据库串|帐套名~是否默认帐套~数据库串
        /// </summary>
        GETDBSTRING,
        /// <summary>
        /// 请求文件版本信息列表 文件名~版本号|文件名~版本号
        /// </summary>
        GETFILEVER,
        /// <summary>
        /// 请求具体文件 GETFILE~*.*
        /// </summary>
        GETFILE
    }
    #endregion

    #region Socket通讯类
    /// <summary>
    /// Socket通讯类
    /// </summary>
    public class SocketHelp
    {
        int sendsize = 10000;
        string TmpPath = string.Empty;

        #region 构造函数
        /// <summary>
        /// 构造函数
        /// </summary>
        public SocketHelp()
        {
            TmpPath = System.AppDomain.CurrentDomain.BaseDirectory + "Tmp\\";
            if (!Directory.Exists(TmpPath))
            {
                Directory.CreateDirectory(TmpPath);
            }
        }
        #endregion

        #region 析构函数
        bool isPosed = false; //释放控制开关, 记录是否已经执行Dispose

        /// <summary>
        /// Dispose实现接口中的方法
        /// </summary>
        public void Dispose() //实现接口中的方法
        {
            try
            {
                Dispose(true);
                System.GC.SuppressFinalize(this);    //手工释放过的，就不要再调用析构函数进行释放了
            }
            catch { }
        }

        /// <summary>
        /// 清理所有正在使用的资源
        /// </summary>
        /// <param name="disposeing">如果应释放托管资源，为 true；否则为 false</param>
        public virtual void Dispose(bool disposeing)
        {
            try
            {
                if (!isPosed)//如果未调用过,则进入释放资源
                {
                    if (disposeing) //手工释放
                    {
                        System.GC.Collect();    //强制进行垃圾回收, 释放所有托管资源
                    }
                }

                isPosed = true;    //已经释放过资源了
            }
            catch { }
        }

        /// <summary>
        /// 垃圾回收
        /// </summary>
        ~SocketHelp()
        {
            Dispose(false);    //若未进行手工释放(程序调用Dispose方法)，由垃圾回收自动进行
        }
        #endregion

        #region Socket发送方法
        /// <summary>
        /// 发送发送消息
        /// </summary>
        /// <param name="_Sck">Socket连接</param>
        /// <param name="_Mes">发送消息</param>
        /// <returns></returns>
        public bool SocketSend(Socket _Sck, string _Mes)
        {

            bool sendResult = true;
            try
            {
                byte[] sendbyte = Encoding.UTF8.GetBytes(_Mes.ToString());
                sendResult = SocketSend(_Sck, TypeSocket.MESSAGES, sendbyte);
            }
            catch (System.Exception ex)
            {
                sendResult = false;
                throw (ex);
            }
            return sendResult;
        }

        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="_Sck">Socket连接串</param>
        /// <param name="_File">文件(FileInfo)</param>
        /// <returns></returns>
        public bool SocketSend(Socket _Sck, FileInfo _File)
        {
            bool sendResult = true;
            byte[] Buff;
            try
            {
                //发送类型
                SendVarData(_Sck, Encoding.UTF8.GetBytes(TypeSocket.FILE.ToString()));
                //SocketLog.Socketlog("发送类型" + TypeSocket.FILE.ToString(), SocketLogType.SocketInformation);
                //发送包的大小
                SendVarData(_Sck, Encoding.UTF8.GetBytes(_File.Length.ToString()));
                //SocketLog.Socketlog("发送包的大小" + _File.Length.ToString(), SocketLogType.SocketInformation);
                //发送包的个数
                int SendCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(_File.Length) / Convert.ToDouble(sendsize)));
                SendVarData(_Sck, Encoding.UTF8.GetBytes(SendCount.ToString()));
                //SocketLog.Socketlog("发送包的个数" + SendCount.ToString(), SocketLogType.SocketInformation);
                //最后包的大小
                int LastSize = (int)_File.Length - sendsize * (SendCount - 1);
                SendVarData(_Sck, Encoding.UTF8.GetBytes(LastSize.ToString()));
                //SocketLog.Socketlog("最后包的大小" + LastSize.ToString(), SocketLogType.SocketInformation);

                //发送文件名称
                SendVarData(_Sck, Encoding.UTF8.GetBytes(_File.Name));
                //发送文件的MD5
                SendVarData(_Sck, Encoding.UTF8.GetBytes(getMD5Hash(_File.FullName))); 
                FileStream EzoneStream = _File.OpenRead();               

                if (SendCount == 1)
                {
                    Buff = new byte[sendsize];
                    EzoneStream.Read(Buff, 0, Buff.Length);
                    SendVarData(_Sck, Buff);
                    //SocketLog.Socketlog("发送包，且总包数包为1个", SocketLogType.SocketInformation);
                }
                else
                {
                    for (int i = 0; i < SendCount - 1; i++)
                    {
                        Buff = new byte[sendsize];
                        EzoneStream.Read(Buff, 0, Buff.Length);
                        SendVarData(_Sck, Buff);
                        //SocketLog.Socketlog(String.Format("发送第{0}个包", i.ToString()), SocketLogType.SocketInformation);
                    }
                    if (LastSize > 0)
                    {
                        Buff = new byte[LastSize];
                        EzoneStream.Read(Buff, 0, Buff.Length);
                        SendVarData(_Sck, Buff);
                        //SocketLog.Socketlog(String.Format("发送最后一个包，且包的大小为{0}", LastSize.ToString()), SocketLogType.SocketInformation);
                    }
                }
                //接收结果
                sendResult = Convert.ToBoolean(Encoding.UTF8.GetString(ReceiveVarData(_Sck)));
                //SocketLog.Socketlog("发送成功", SocketLogType.SocketInformation);
            }
            catch (System.Exception ex)
            {
                SocketLog.Socketlog(ex.ToString(), SocketLogType.SocketErr);
                sendResult = false;
                throw (ex);
            }
            return sendResult;
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="_Sck">Socket连接</param>
        /// <param name="_Type">发送类型</param>
        /// <param name="Send_byte">发送内容</param>
        /// <returns></returns>
        private bool SocketSend(Socket _Sck, TypeSocket _Type, byte[] Send_byte)
        {
            bool sendResult = true;
            byte[] Buff;
            try
            {
                if (_Type != TypeSocket.FILE)
                {
                    Send_byte = Encoding.UTF8.GetBytes(SckDESEncrypt.EncryptDES(Encoding.UTF8.GetString(Send_byte)));
                }
                //发送类型
                SendVarData(_Sck, Encoding.UTF8.GetBytes(_Type.ToString()));
                //SocketLog.Socketlog("发送类型" + _Type.ToString(), SocketLogType.SocketInformation);
                //发送包的大小
                SendVarData(_Sck, Encoding.UTF8.GetBytes(Send_byte.Length.ToString()));
                //SocketLog.Socketlog("发送包的大小" + Send_byte.Length.ToString(), SocketLogType.SocketInformation);
                //发送包的个数
                int SendCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(Send_byte.Length) / Convert.ToDouble(sendsize)));
                SendVarData(_Sck, Encoding.UTF8.GetBytes(SendCount.ToString()));
                //SocketLog.Socketlog("发送包的个数" + SendCount.ToString(), SocketLogType.SocketInformation);
                //最后包的大小
                int LastSize = Send_byte.Length - sendsize * (SendCount - 1);
                SendVarData(_Sck, Encoding.UTF8.GetBytes(LastSize.ToString()));
                //SocketLog.Socketlog("最后包的大小" + LastSize.ToString(), SocketLogType.SocketInformation);
                if (SendCount == 1)
                {
                    SendVarData(_Sck, Send_byte);
                    //SocketLog.Socketlog("发送包，且总包数包为1个", SocketLogType.SocketInformation);
                }
                else
                {
                    for (int i = 0; i < SendCount - 1; i++)
                    {
                        Buff = new byte[sendsize];
                        Array.Copy(Send_byte, i * sendsize, Buff, 0, Buff.Length);
                        SendVarData(_Sck, Buff);
                        //SocketLog.Socketlog(String.Format("发送第{0}个包", i.ToString()), SocketLogType.SocketInformation);
                    }
                    if (LastSize > 0)
                    {
                        Buff = new byte[LastSize];
                        Array.Copy(Send_byte, (SendCount - 1) * sendsize, Buff, 0, Buff.Length);
                        SendVarData(_Sck, Buff);
                        //SocketLog.Socketlog(String.Format("发送最后一个包，且包的大小为{0}", LastSize.ToString()), SocketLogType.SocketInformation);
                    }
                }
                //接收结果
                sendResult = Convert.ToBoolean(Encoding.UTF8.GetString(ReceiveVarData(_Sck)));
                //SocketLog.Socketlog("发送成功", SocketLogType.SocketInformation);
            }
            catch (System.Exception ex)
            {
                SocketLog.Socketlog(ex.ToString(), SocketLogType.SocketErr);
                sendResult = false;
                throw (ex);
            }
            return sendResult;
        }
        #endregion

        #region Socket接收方法
        /// <summary>
        /// 接收
        /// </summary>
        /// <param name="_Sck">Socket连接</param>
        /// <returns></returns>
        public Hashtable SocketReceive(Socket _Sck)
        {
            bool Result = true;
            Hashtable Rec_Hht = new Hashtable();//最后的结果
            byte[] Buff;
            string Rec_Type = string.Empty; ;//定义类型
            string Rec_Context = string.Empty; ;//定义接收的数据
            string FileTMPName = string.Empty; //定义临时文件名
            string FileName = string.Empty;
            string FileMd5 = string.Empty;//文件的MD5
            int Rec_Length = 0; //接收数据的总大小
            int Rec_Count = 0;//接收包的总数
            int Rec_Last = 0;//最后包的大小
            try
            {
                Rec_Type = Encoding.UTF8.GetString(ReceiveVarData(_Sck));
                //SocketLog.Socketlog("接收类型" + Rec_Type.ToString(), SocketLogType.SocketInformation);
                TypeSocket _Type = (TypeSocket)Enum.Parse(typeof(TypeSocket), Rec_Type, true);
                Rec_Length = Convert.ToInt32(Encoding.UTF8.GetString(ReceiveVarData(_Sck)));
                //SocketLog.Socketlog("接收数据的总大小" + Rec_Length.ToString(), SocketLogType.SocketInformation);
                Rec_Count = Convert.ToInt32(Encoding.UTF8.GetString(ReceiveVarData(_Sck)));
                //SocketLog.Socketlog("接收包的总数" + Rec_Count.ToString(), SocketLogType.SocketInformation);
                Rec_Last = Convert.ToInt32(Encoding.UTF8.GetString(ReceiveVarData(_Sck)));
                //SocketLog.Socketlog("最后包的大小" + Rec_Last.ToString(), SocketLogType.SocketInformation);
                int Buff_Start = 0;
                if (_Type == TypeSocket.FILE)
                {
                    FileName = Encoding.UTF8.GetString(ReceiveVarData(_Sck));
                    FileTMPName = Guid.NewGuid().ToString();
                    string FilePath = TmpPath + FileTMPName;
                    //接收文件的MD5
                    FileMd5 = Encoding.UTF8.GetString(ReceiveVarData(_Sck));
                    FileStream MyFileStream = new FileStream(FilePath, FileMode.Create, FileAccess.Write);
                    if (Rec_Count == 1)
                    {
                        Buff = ReceiveVarData(_Sck);
                        MyFileStream.Write(Buff, 0, Buff.Length);
                        //SocketLog.Socketlog("接收包，且包的个数为1" + Rec_Context.ToString(), SocketLogType.SocketInformation);
                    }
                    else
                    {
                        for (int i = 0; i < Rec_Count; i++)
                        {
                            byte[] RecTmp = ReceiveVarData(_Sck);
                            MyFileStream.Write(RecTmp, 0, RecTmp.Length);
                            //SocketLog.Socketlog("接收包，当前包为：" + i.ToString(), SocketLogType.SocketInformation);
                        }
                    }
                    MyFileStream.Close();
                    Rec_Context = FilePath;
                }
                else
                {
                    if (Rec_Count == 1)
                    {
                        Rec_Context = SckDESEncrypt.DecryptDES(Encoding.UTF8.GetString(ReceiveVarData(_Sck)));
                        //SocketLog.Socketlog("接收包，且包的个数为1" + Rec_Context.ToString(), SocketLogType.SocketInformation);
                    }
                    else
                    {
                        Buff = new byte[Rec_Length];
                        for (int i = 0; i < Rec_Count; i++)
                        {
                            byte[] RecTmp = ReceiveVarData(_Sck);
                            Array.Copy(RecTmp, 0, Buff, Buff_Start, RecTmp.Length);
                            Buff_Start += RecTmp.Length;
                            //SocketLog.Socketlog("接收包，当前包为：" + i.ToString(), SocketLogType.SocketInformation);
                        }
                        Rec_Context = SckDESEncrypt.DecryptDES(Encoding.UTF8.GetString(Buff));
                    }
                }
            }
            catch (System.Exception ex)
            {
                SocketLog.Socketlog(ex.ToString(), SocketLogType.SocketErr);
                Result = false;
                Rec_Context = ex.Message.ToString();
                throw (ex);
            }
            finally
            {
                SendVarData(_Sck, Encoding.UTF8.GetBytes(Result.ToString()));
                if (Result)
                {
                    Rec_Hht.Add("Stats", 0);
                    Rec_Hht.Add("Type", Rec_Type.Trim());
                    Rec_Hht.Add("Value", Rec_Context);
                    Rec_Hht.Add("FileName", FileName);
                    Rec_Hht.Add("FileMd5", FileMd5);
                }
                else
                {
                    Rec_Hht.Add("Stats", 1);
                    Rec_Hht.Add("Type", TypeSocket.MESSAGES.ToString());
                    Rec_Hht.Add("Value", Rec_Context);
                    Rec_Hht.Add("FileName", FileName);
                    Rec_Hht.Add("FileMd5", FileMd5);
                }
            }
            return Rec_Hht;
        }

        #endregion

        #region 底层发送接收方法
        private int SendVarData(Socket s, byte[] data)
        {
            int total = 0;
            try
            {
                int size = data.Length;
                int dataleft = size;
                int sent;
                byte[] datasize = new byte[4];
                datasize = BitConverter.GetBytes(size);
                sent = s.Send(datasize);

                while (total < size)
                {
                    sent = s.Send(data, total, dataleft, SocketFlags.None);
                    total += sent;
                    dataleft -= sent;
                }
            }
            catch (System.Exception ex)
            {
                SocketLog.Socketlog(ex.ToString(), SocketLogType.SocketErr);
                throw (ex);
            }
            return total;
        }

        private byte[] ReceiveVarData(Socket s)
        {
            int total = 0;
            byte[] data = null;
            int recv;
            try
            {
                byte[] datasize = new byte[4];
                recv = s.Receive(datasize, 0, 4, SocketFlags.None);
                int size = BitConverter.ToInt32(datasize, 0);
                int dataleft = size;
                data = new byte[size];
                while (total < size)
                {
                    recv = s.Receive(data, total, dataleft, SocketFlags.None);
                    if (recv == 0)
                    {
                        data = null;
                        break;
                    }
                    total += recv;
                    dataleft -= recv;
                }
            }
            catch (System.Exception ex)
            {
                SocketLog.Socketlog(ex.ToString(), SocketLogType.SocketErr);
                throw (ex);
            }
            return data;
        }
        #endregion

        #region 计算文件的MD5
        private string getMD5Hash(string pathName)
        {
            string strResult = "";
            string strHashData = "";

            byte[] arrbytHashValue;
            System.IO.FileStream oFileStream = null;
            System.Security.Cryptography.MD5CryptoServiceProvider oMD5Hasher = new System.Security.Cryptography.MD5CryptoServiceProvider();
            try
            {
                oFileStream = new System.IO.FileStream(pathName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
                arrbytHashValue = oMD5Hasher.ComputeHash(oFileStream);//计算指定Stream 对象的哈希值
                oFileStream.Close();
                //由以连字符分隔的十六进制对构成的String，其中每一对表示value 中对应的元素；例如“F-2C-4A”
                strHashData = System.BitConverter.ToString(arrbytHashValue);
                //替换-
                strHashData = strHashData.Replace("-", "");
                strResult = strHashData;
            }
            catch (System.Exception ex)
            {
                throw (ex);
            }
            return strResult;
        }
        #endregion
    }
    #endregion

    #region Socket日志类
    /// <summary>
    /// Socket日志类型
    /// </summary>
    public enum SocketLogType
    {
        SocketErr,
        SocketInformation
    }

    /// <summary>
    /// Socket日志登记
    /// </summary>
    public class SocketLog
    {
        static string SckLogDir = string.Empty;

        static SocketLog()
        {
            SckLogDir = System.AppDomain.CurrentDomain.BaseDirectory + "log\\";
            if (!Directory.Exists(SckLogDir))
            {
                Directory.CreateDirectory(SckLogDir);
            }
        }
        /// <summary>
        /// Socket日志记录
        /// </summary>
        /// <param name="logText">内容</param>
        /// <param name="lType">类型</param>
        public static void Socketlog(string logText, SocketLogType lType)
        {
            string fileName = string.Empty;
            switch (lType)
            {
                case SocketLogType.SocketErr:
                    {
                        fileName = SckLogDir + DateTime.Now.ToString("yyMMdd") + ".SocketErr";
                        break;
                    }
                case SocketLogType.SocketInformation:
                    {
                        fileName = SckLogDir + DateTime.Now.ToString("yyMMdd") + ".SocketLog";
                        break;
                    }
            }
            string[] textArray = new string[3] { DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss]"), logText, "\r\n-------------\r\n" };
            logText = string.Concat(textArray);
            byte[] buffer = Encoding.UTF8.GetBytes(logText);
            FileStream stream = null;
            try
            {
                stream = File.Open(fileName, FileMode.OpenOrCreate);
                stream.Seek((long)0, SeekOrigin.End);
                stream.Write(buffer, 0, buffer.Length);
            }
            catch (Exception)
            {
            }
            finally
            {
                if (stream != null)
                    stream.Close();
            }
        }
    }
    #endregion

    #region 消息加解密
    public class SckDESEncrypt
    {
        /// <summary>
        /// 默认密钥向量
        /// </summary>
        private static byte[] Keys = { 0x90, 0x12, 0xEF, 0x34, 0xCD, 0x56, 0xAB, 0x78 };
        //private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
        private static string CryptKey = "~!@#$%^&";

        /// <summary>
        /// DES加密字符串
        /// </summary>
        /// <param name="encryptString">待加密的字符串</param>
        /// <param name="encryptKey">加密密钥,要求为8位</param>
        /// <returns>加密成功返回加密后的字符串，失败返回源串</returns>
        public static string EncryptDES(string encryptString) //, string encryptKey)
        {
            try
            {
                byte[] rgbKey = Encoding.UTF8.GetBytes(CryptKey.Substring(0, 8));
                byte[] rgbIV = Keys;
                byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);
                DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider();
                MemoryStream mStream = new MemoryStream();
                CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
                cStream.Write(inputByteArray, 0, inputByteArray.Length);
                cStream.FlushFinalBlock();
                return Convert.ToBase64String(mStream.ToArray());
            }
            catch
            {
                return encryptString;
            }
        }

        /// <summary>
        /// DES解密字符串
        /// </summary>
        /// <param name="decryptString">待解密的字符串</param>
        /// <param name="decryptKey">解密密钥,要求为8位,和加密密钥相同</param>
        /// <returns>解密成功返回解密后的字符串，失败返源串</returns>
        public static string DecryptDES(string decryptString) //, string decryptKey)
        {
            try
            {
                byte[] rgbKey = Encoding.UTF8.GetBytes(CryptKey);
                byte[] rgbIV = Keys;
                byte[] inputByteArray = Convert.FromBase64String(decryptString);
                DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider();
                MemoryStream mStream = new MemoryStream();
                CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
                cStream.Write(inputByteArray, 0, inputByteArray.Length);
                cStream.FlushFinalBlock();
                return Encoding.UTF8.GetString(mStream.ToArray());
            }
            catch
            {
                return decryptString;
            }
        }
    }
    #endregion
}
